﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;

namespace Plan
{
    public class ScheduleInstance
    {
        protected readonly List<Subject> _subjects = new List<Subject>();
        protected readonly List<Group> _groups = new List<Group>();
        protected readonly List<ClassRoom> _classRooms = new List<ClassRoom>();
        private readonly List<Teacher> _teachers = new List<Teacher>();

        public List<Teacher> Teachers
        {
            get { return _teachers; }
        }

        private string _name = "";
        private string _comment = "";
        private static readonly Random random = new Random();
        private int _lessonsPerDay = 9;
        private int _dayCount = 5;
        private int _optimalLessonCount;
        /// <summary>
        /// Macierz, gdize M[g_i,t_j,s_k] = true, jeśli taka kombinacja group-teacher-subject ma sens,
        /// czyli wtedy, gdy nauczyciel t_j uczy przedmiotu s_k oraz grupa g_i ma ten przedmiot.
        /// </summary>
        public bool[, ,] M;
        int meaningFullValusCount;

        /// <summary>
        /// Zjednowymiarowienie macierzy M. Zwraca M[i / SubjectCount / TeacherCount, i / SubjectCount, i % SubjectCount].
        /// </summary>
        /// <param name="i">Indeks elementu macierzy M tak, jakby była jednowymiarowa.</param>
        public unsafe bool GetIthMElement(int i)
        {
            fixed (bool* _ptr = M)
            {
                return _ptr[i];
            }
            //return M[i / SubjectCount / TeacherCount, i / SubjectCount, i % SubjectCount];
        }
        public int MeaningFullValusCount
        {
            get { return meaningFullValusCount; }
        }

        internal void GenerateMatrix()
        {
            M = new bool[GroupCount, TeacherCount, SubjectCount];
            int n = 0;

            for (int i = 0; i < GroupCount; i++)
                for (int j = 0; j < TeacherCount; j++)
                    for (int k = 0; k < SubjectCount; k++)
                        if (_groups[i].Subjects.Contains(_subjects[k].Id) && _teachers[j].Subjects.Contains(_subjects[k].Id))
                        {
                            M[i, j, k] = true;
                            n++;
                        }
            meaningFullValusCount = n;
        }
        /// <summary>
        /// GroupCount * TeacherCount * SubjectCount; => długość permutacji
        /// </summary>
        public int Size
        {
            get
            {
                return Math.Max(GroupCount * TeacherCount * SubjectCount, _lessonsPerDay * _dayCount * _classRooms.Count);
            }
        }
        /// <summary>
        /// GroupCount*SubjectPerGroup &lt;= Size
        /// </summary>
        public bool Solvable
        {
            get { return meaningFullValusCount <= TotalTime * ClassroomsCount; }
        }
        /// <summary>
        /// _lessonsPerDay * _dayCount
        /// </summary>
        public int TotalTime
        {
            get
            {
                return _lessonsPerDay * _dayCount;
            }
        }

        public void Add(Subject item)
        {
            _subjects.Add(item);
        }

        public void Add(Group item)
        {
            _groups.Add(item);
        }

        public void Add(ClassRoom item)
        {
            _classRooms.Add(item);
        }
        public void Add(Teacher item)
        {
            _teachers.Add(item);
        }
        public ScheduleInstance()
        {
        }

        public List<Subject> Subjects
        {
            get { return _subjects; }
        }
        public List<Group> Groups
        {
            get { return _groups; }
        }


        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public string Comment
        {
            get { return _comment; }
            set { _comment = value; }
        }

        public int DayCount
        {
            get { return _dayCount; }
            set
            {
                _dayCount = value;
            }
        }
        /// <summary>
        /// _subjects.Count;
        /// </summary>
        public int SubjectCount
        {
            get
            {
                return _subjects.Count;
            }
        }
        /// <summary>
        /// _teachers.Count;
        /// </summary>
        public int TeacherCount
        {
            get
            {
                return _teachers.Count;
            }
        }

        public int LessonsPerDay
        {
            get { return _lessonsPerDay; }
            set
            {
                _lessonsPerDay = value;
            }
        }

        public List<ClassRoom> Classrooms
        {
            get { return _classRooms; }
        }

        public int GroupCount
        {
            get { return _groups.Count; }
        }

        public int ClassroomsCount
        {
            get { return _classRooms.Count; }
        }
        /// <summary>
        /// Math.Max(1, SubjectPerGroup/DayCount)
        /// </summary>
        public int OptimalLessonCount
        {
            get
            {
                return Math.Max(1, SubjectCount / DayCount);
                //return _optimalLessonCount;
            }
        }

        public struct GenerateParams
        {
            public int GroupCount { get; set; }

            public int SubjectCount { get; set; }

            public int RoomCount { get; set; }

            public int TeacherCount { get; set; }
        }

        public static ScheduleInstance GenerateRandom(Random generator, GenerateParams generateParams)
        {
            var instance = new ScheduleInstance();
            for (int i = 0; i < generateParams.GroupCount; i++)
                instance.Add(new Group { Id = i });

            for (int i = 0; i < generateParams.SubjectCount; i++)
                instance.Add(new Subject { Id = i });

            for (int i = 0; i < generateParams.RoomCount; i++)
                instance.Add(new ClassRoom { Id = i });
            for (int i = 0; i < generateParams.TeacherCount; i++)
                instance.Add(new Teacher { Id = i });
            //for (int i = 0; i < generateParams.GroupCount; i++)
            //  for (int j = 0; j < generateParams.SubjectCount; j++)
            //    instance.Groups[i].Subjects.Add(instance.Subjects[generator.Next(0, generateParams.SubjectCount)].Id);

            return instance;
        }

        public static ScheduleInstance GenerateRandom(GenerateParams generateParams)
        {
            return GenerateRandom(random, generateParams);
        }

        private static double Pow(double a)
        {
            return a * a;
        }

        internal void Init()
        {
            GenerateMatrix();
        }
        public override string ToString()
        {
            var str = new StringBuilder();
            str.AppendLine("Przypisania");
            str.AppendLine(
                _groups.Select(g => "g" + g.Id + ": " + g.Subjects.Aggregate("[ ",
                (string s, int x) => s + "s" + x + " ")).Aggregate(
                (s1, s2) => s1 + "]" + Environment.NewLine + s2) + "]");
            str.AppendLine(
                _teachers.Select(t => "t" + t.Id + ": " + t.Subjects.Aggregate("[ ",
                (string s, int x) => s + "s" + x + " ")).Aggregate(
                (s1, s2) => s1 + "]" + Environment.NewLine + s2) + "]");
            return str.ToString();
        }
    }
}